﻿/*
 *  Source code courtesy of the desktopWeb.CodePlex.com community project. See MS-PL license on Codeplex.com. 
 *  This is beta code not intended for a production application.
 */

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.SharePoint.Client;
using System.Linq.Expressions;
using System.Xml;
using System.IO;
using System.Globalization;

namespace MyDataTestProject
{
  [TestClass]
  public class ExternalLists
  {
    [TestMethod]
    public void ViewSchema()
    {
      string InArgument_Url = "http://localhost";
      string InArgument_Title = "ExternalList_Test";

      using (ClientContext clientContext = new ClientContext(InArgument_Url))
      {

        List externalList = clientContext.Web.Lists.GetByTitle(InArgument_Title);

        // HtmlSchemaXm - Gets the entire schema for the view.
        clientContext.Load(
            externalList.Views,
            viewCollection => viewCollection.Include(
                view => view.ViewFields,
                view => view.HtmlSchemaXml));

        // This tells us how many list items we can retrieve.
        clientContext.Load(clientContext.Site,s => s.MaxItemsPerThrottledOperation);

        clientContext.ExecuteQuery();

        Assert.IsTrue(externalList.Views.Count > 0);
      }
    }

    [TestMethod]
    public void GetItems()
    {
      //InArguments
      string url = "http://localhost";
      string listTitle = "ExternalList_Test";

      using (ClientContext clientContext = new ClientContext(url))
      {
         List externalList = clientContext.Web.Lists.GetByTitle(listTitle);

          // To properly construct the CamlQuery and 
          // ClientContext.LoadQuery,
          // we need some View data of the Virtual List.
          // In particular, the View will give us the CamlQuery 
          // Method and Fields.
          clientContext.Load(
              externalList.Views,
              viewCollection => viewCollection.Include(
                  view => view.ViewFields,
                  view => view.HtmlSchemaXml));

          // This tells us how many list items we can retrieve.
          clientContext.Load(clientContext.Site, s => s.MaxItemsPerThrottledOperation);

          clientContext.ExecuteQuery();

          // Let's just pick the first View.
          View targetView = externalList.Views[0];
          string method = ReadMethodFromViewXml(
              targetView.HtmlSchemaXml);
          ViewFieldCollection viewFields = targetView.ViewFields;

          CamlQuery vlQuery = CreateCamlQuery(
              clientContext.Site.MaxItemsPerThrottledOperation,
              method,
              viewFields);

          Expression<Func<ListItem, object>>[] listItemExpressions = CreateListItemLoadExpressions(viewFields);

          ListItemCollection listItemCollection = externalList.GetItems(vlQuery);

          // Note: Due to limitation, you currently cannot use 
          // ClientContext.Load.
          //       (you'll get InvalidQueryExpressionException)
          IEnumerable<ListItem> resultData = clientContext.LoadQuery(listItemCollection.Include(listItemExpressions));

          clientContext.ExecuteQuery();

          Assert.IsTrue(resultData.Count() > 0);
      }
    }


    /// <summary>
    /// Parses the viewXml and returns the Method value.
    /// </summary>        
    private static string ReadMethodFromViewXml(string viewXml)
    {
        XmlReaderSettings readerSettings = new XmlReaderSettings();
        readerSettings.ConformanceLevel = ConformanceLevel.Fragment;

        XmlReader xmlReader = XmlReader.Create(
            new StringReader(viewXml), readerSettings);
        while (xmlReader.Read())
        {
            switch (xmlReader.NodeType)
            {
                case XmlNodeType.Element:
                    if (xmlReader.Name == "Method")
                    {
                        while (xmlReader.MoveToNextAttribute())
                        {
                            if (xmlReader.Name == "Name")
                            {
                                return xmlReader.Value;
                            }
                        }
                    }
                    break;
            }
        }

        throw new Exception("Unable to find Method in View XML");
    }


    /// <summary>
    /// Creates a CamlQuery based on the inputs.
    /// </summary>        
    private static CamlQuery CreateCamlQuery(
        uint rowLimit, string method, ViewFieldCollection viewFields)
    {
        CamlQuery query = new CamlQuery();

        XmlWriterSettings xmlSettings = new XmlWriterSettings();
        xmlSettings.OmitXmlDeclaration = true;

        StringBuilder stringBuilder = new StringBuilder();
        XmlWriter writer = XmlWriter.Create(
            stringBuilder, xmlSettings);

        writer.WriteStartElement("View");

        // Specifies we want all items, regardless of folder level.
        writer.WriteAttributeString("Scope", "RecursiveAll");

        writer.WriteStartElement("Method");
        writer.WriteAttributeString("Name", method);
        writer.WriteEndElement();  // Method

        if (viewFields.Count > 0)
        {
            writer.WriteStartElement("ViewFields");
            foreach (string viewField in viewFields)
            {
                if (!string.IsNullOrEmpty(viewField))
                {
                    writer.WriteStartElement("FieldRef");
                    writer.WriteAttributeString("Name", viewField);
                    writer.WriteEndElement();  // FieldRef
                }
            }
            writer.WriteEndElement();  // ViewFields
        }

        writer.WriteElementString("RowLimit", rowLimit.ToString(CultureInfo.InvariantCulture));

        writer.WriteEndElement();  // View

        writer.Close();

        query.ViewXml = stringBuilder.ToString();

        return query;
    }

    /// <summary>
    /// Returns an array of Expression used in 
    /// ClientContext.LoadQuery to retrieve 
    /// the specified field data from a ListItem.        
    /// </summary>        
    private static Expression<Func<ListItem, object>>[] CreateListItemLoadExpressions(ViewFieldCollection viewFields)
    {
        List<Expression<Func<ListItem, object>>> expressions =
            new List<Expression<Func<ListItem, object>>>();

        foreach (string viewFieldEntry in viewFields)
        {
            // Note: While this may look unimportant, 
            // and something we can skip, in actuality,
            //       we need this step.  The expression should 
            // be built with local variable.                
            string fieldInternalName = viewFieldEntry;

            Expression<Func<ListItem, object>>
                retrieveFieldDataExpression =
                listItem => listItem[fieldInternalName];

            expressions.Add(retrieveFieldDataExpression);
        }

        return expressions.ToArray();
    }

    public void AddItem()
    {
      //InArguments
      string url = "http://localhost";
      string listTitle = "ExternalList_Test";

      using (ClientContext clientContext = new ClientContext(url))
      {
        List externalList = clientContext.Web.Lists.GetByTitle(listTitle);

        ListItem newItem = externalList.AddItem(new ListItemCreationInformation());
        
        newItem["Name"] = "new item";
        newItem["Value"] = "My Value for new item";
        // Set all required fields.
        newItem.Update();
        clientContext.ExecuteQuery();

      }
    }

    public void UpdateItem()
    {
      //InArguments
      string url = "http://localhost";
      string listTitle = "ExternalList_Test";

      using (ClientContext clientContext = new ClientContext(url))
      {
        List externalList = clientContext.Web.Lists.GetByTitle(listTitle);


        //1. Get <BdcIdentity>
        //-- ID=__bg40004300 - How to get?
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Contains>
                      <FieldRef Name='Name'/>
                      <Value Type='Text'>Test</Value>
                    </Contains>
                  </Where>
                </Query>
                <RowLimit>100</RowLimit>
                <ViewFields>
                  <FieldRef Name='ID'/>
                  <FieldRef Name='Name'/>
                  <FieldRef Name='Value'/>
                </ViewFields>
              </View>";
        ListItemCollection listItems = externalList.GetItems(camlQuery);

        clientContext.Load(listItems);
        clientContext.ExecuteQuery();

        string bdcIdentity = string.Empty;

        if (listItems.Count > 0)
          bdcIdentity = listItems[0]["BdcIdentity"].ToString();

        ListItem specificItem = externalList.GetItemById(bdcIdentity);
        specificItem["Name"] = "Test Content Part";
        specificItem["Value"] = "value is stored in SQL, but rendered in SharePoint";
        // Set all required fields
        specificItem.Update();
        clientContext.ExecuteQuery();
      }
    }

    [TestMethod]
    public void DeleteItem()
    {
      //InArguments
      string url = "http://localhost";
      string listTitle = "ExternalList_Test";

      using (ClientContext clientContext = new ClientContext(url))
      {
        List externalList = clientContext.Web.Lists.GetByTitle(listTitle);

        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='ID'/>
                      <Value Type='Number'>8</Value>
                    </Eq>
                  </Where>
                </Query>
                <RowLimit>1</RowLimit>
                <ViewFields>
                  <FieldRef Name='ID'/>
                  <FieldRef Name='Name'/>
                  <FieldRef Name='Value'/>
                </ViewFields>
              </View>";
        ListItemCollection listItems = externalList.GetItems(camlQuery);
        clientContext.Load(
             listItems,
             items => items
                 .Include(
                     item => item["ID"],
                     item => item["Name"],
                     item => item["Value"],
                     item => item["BdcIdentity"])

                  );
        clientContext.ExecuteQuery();

        string bdcIdentity = string.Empty;

        if (listItems.Count > 0)
          bdcIdentity = listItems[0]["BdcIdentity"].ToString();

        ListItem specificItem = externalList.GetItemById(bdcIdentity);

        // Set all required fields
        specificItem.DeleteObject();
        clientContext.ExecuteQuery();
      }
    }

  }
}
